package org.hzero.system.copy;

import io.choerodon.core.exception.CommonException;
import org.hzero.system.annotation.KnifeField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName: ThBeanUtil
 * @Description: 拷贝属性
 * @author: tianhao.luo@hand-china.com
 * @date: 2020/11/28  9:41
 */
public final class KnifeBeanUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(KnifeBeanUtil.class);

    /**
     * 获取当前类的所有属性
     *
     * @param aClass    类
     * @param fieldList 空的属性list
     */
    public static void addAllFields(Class<?> aClass, List<Field> fieldList) {
        Class<?> superclass = aClass.getSuperclass();
        if (superclass != null) {
            addAllFields(superclass, fieldList);
        }
        fieldList.addAll(Arrays.asList(aClass.getDeclaredFields()));
    }

    /**
     * 配合HalmToEbField注解使用
     * 不负责业务逻辑处理，如果有复杂的处理需要自己在调用此方法后进行处理
     *
     * @param source 来源
     * @param target 去向
     * @param <T>    占位符
     * @param <S>    占位符
     */
    public static <T, S> void copy(T source, S target,String... ignoreTargetFields) {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        //获取来源对象的class类对象，用来获取原值
        Class<?> tClass = source.getClass();
        List<Field> allSourceFieldList = new ArrayList<>();
        //添加所有属性
        addAllFields(tClass, allSourceFieldList);
        //过滤掉常量属性
        List<Field> noFinalSourceFieldList = allSourceFieldList.stream().filter(item -> !"public static final".equals(Modifier.toString(item.getModifiers()))).collect(Collectors.toList());
        //将来源属性做成键值对
        Map<String, List<Field>> map = noFinalSourceFieldList.stream().collect(Collectors.groupingBy(Field::getName));
        //准备反射目的对象
        Class<?> sClass = target.getClass();
        //获取目的对象的所有属性
        List<Field> allTargetFieldList = new ArrayList<>();
        addAllFields(sClass,allTargetFieldList);
        //校验是否有不匹配的属性名
        List<String> sourceFieldNameList = noFinalSourceFieldList.stream().map(Field::getName).collect(Collectors.toList());
        List<String> targetFieldNameList = new ArrayList<>();
        //获取需要过滤掉的属性
        List<String> ignoreList = ignoreTargetFields != null ? Arrays.asList(ignoreTargetFields) : null;
        for (Field field :
                allTargetFieldList) {
            KnifeField knifeField = field.getAnnotation(KnifeField.class);
            //标记了ThField属性的，并且不在忽略列表内的属性加入需要拷贝的属性中
            if (null != knifeField && (ignoreList == null || !ignoreList.contains(field.getName()))) {
                targetFieldNameList.add(knifeField.name());
            }
        }
        //判断sourceFieldNameList是否包含了targetFieldNameList中所有的属性，如果没有则报错
        StringBuilder sb = new StringBuilder();
        for (String targetFieldName :
                targetFieldNameList) {
            if (sourceFieldNameList.stream().noneMatch(item -> item.equals(targetFieldName))) {
                if (sb.length()==0){
                    sb.append(targetFieldName);
                }else {
                    sb.append(",").append(targetFieldName);
                }
            }
        }

        Assert.isTrue(sb.length() ==0,"ThBeanUtil将要出错！ThField注解中："+sb.toString()+"属性并不存在于来源对象中");
        
        for (Field field :
                allTargetFieldList) {
            KnifeField knifeField = field.getAnnotation(KnifeField.class);
            if (null != knifeField) {
                //取值
                String thFieldName =  null;
                try {
                    thFieldName = knifeField.name();
                    Field sourceField = map.get(thFieldName).get(0);
                    //标记了ThField属性的，并且不在忽略列表内的属性加入需要拷贝的属性中
                    if (null != sourceField && (ignoreList == null || !ignoreList.contains(field.getName()))) {
                        sourceField.setAccessible(true);
                        Object o = sourceField.get(source);
                        field.setAccessible(true);
                        field.set(target, o);
                    }
                } catch (Exception e) {
                    LOGGER.error(e.getMessage());
                    LOGGER.error("{}属性取/赋值出错",thFieldName);
                }
            }
        }
    }

    /**
     * 校验被ApiModelProperty标记的属性必填是否通过
     *
     * @param obj 待校验的对象
     * @param <T> 占位符
     */
    public static <T> void checkFieldRequire(T obj) {
        Class<?> objClass = obj.getClass();
        List<Field> allDbFieldList = new ArrayList<>();
        addAllFields(objClass, allDbFieldList);
        List<Field> allDbFilterFieldList = allDbFieldList.stream().filter(item -> !"public static final".equals(Modifier.toString(item.getModifiers()))).collect(Collectors.toList());
        boolean flag = false;
        StringBuilder sb = new StringBuilder();
        for (Field field :
                allDbFilterFieldList) {
            NotNull notNull = field.getAnnotation(NotNull.class);
            NotBlank notBlank = field.getAnnotation(NotBlank.class);
            try {
                if (null != notBlank ) {
                    field.setAccessible(true);
                    Object o = field.get(obj);
                    String str = (String) o;
                    if (!StringUtils.isEmpty(str)) {
                        flag = true;
                        if (sb.length() == 0) {
                            sb.append(field.getName());
                        } else {
                            sb.append(",").append(field.getName());
                        }
                    }
                }
                if (null != notNull) {
                    field.setAccessible(true);
                    Object o = field.get(obj);
                    if (o == null) {
                        flag = true;
                        if (sb.length() == 0) {
                            sb.append(field.getName());
                        } else {
                            sb.append(",").append(field.getName());
                        }
                    }
                }
            } catch (IllegalAccessException e) {
                throw new CommonException("获取ApiModelProperty的require出错");
            }
        }
        if (flag) {
            throw new CommonException("有必填属性未填充:" + sb.toString());
        }
    }
}
